package com.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * @Description 接受/处理/相应客户端
 * @Author
 * @Date 2020/4/21 20:22
 **/
public class MyWebsocketHandler extends SimpleChannelInboundHandler<Object> {
    private WebSocketServerHandshaker handshaker;

    private static final String WEB_SOCKET_URL ="ws://127.0.0.1/websocket";

    //处理请求的核心方法
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        //处理客户端向服务端发起http捂手请求的业务
        if(msg instanceof FullHttpRequest){
            handHttpRequest(ctx, (FullHttpRequest) msg);
        }else if(msg instanceof WebSocketFrame){
            //处理webSocket连接业务
            handWebsocketFrame(ctx, (WebSocketFrame) msg);
        }
    }
    /** 
     *  处理客户端向服务端发起的http握手请求业务请求
     * @param  
     * @return  
     * @Author songxulin
     * @date 2020/4/22 19:55 
    */ 
    private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req){
        if(!req.getDecoderResult().isSuccess()||!("websocket".equals(req.headers().get("Upgrade")))){
            sendHttpResponse(ctx,req,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory wsfactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL,null,false);
        handshaker = wsfactory.newHandshaker(req);
        if(handshaker == null){
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        }else {
            handshaker.handshake(ctx.channel(),req);
        }
    }
    /** 
     *  处理客户端与服务器端之前的websocket业务
     * @param
     * @return
     * @Author songxulin
     * @date 2020/4/22 20:45 
    */ 
    private void handWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame){
        //判断是否是关闭websocket
        if(frame instanceof CloseWebSocketFrame){
            handshaker.close(ctx.channel(),((CloseWebSocketFrame) frame).retain());
        }

        if(frame instanceof PingWebSocketFrame){
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
        }

        //判断是否是二进制消息
        if(frame instanceof BinaryWebSocketFrame){
            System.out.println("目前我们不支持二进制消息");
            throw new RuntimeException("【"+this.getClass().getName()+"】不支持此消息");
        }

        //返回应答消息
        //获取客户端向服务器端发送的消息
        String text = ((TextWebSocketFrame) frame).text();
        System.out.println("服务器端收到客户端消息===>>>"+text);
        TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + "====>>>" + text);
        //群发消息，服务器端向每个连接上来的客户端群发消息
        NettyConfig.group.writeAndFlush(tws);

    }
    /** 
     *  服务端向客户端响应消息
     * @param  
     * @return  
     * @Author songxulin
     * @date 2020/4/22 20:35 
    */ 
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res){
        if(res.getStatus().code()!=200){
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
            //服务端向客户端发送数据
            ChannelFuture channelFuture = ctx.channel().writeAndFlush(res);
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }


    }

    //客户端与服务端连接开启时调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.add(ctx.channel());
        System.out.println("客户端与服务端连接开启");
    }
    //客户端与服务端连接结束时调用
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.remove(ctx.channel());
        System.out.println("客户端与服务端连接关闭");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
